package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.common.util.DateUtil;
import com.foreveross.crawl.common.util.RandomBrowserVersion;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.domain.airfreight.single.SinglePlaneInfoEntity;
import com.foreveross.proxyip.ProxyIpProviderFactory;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.util.NameValuePair;

public class ThaiAirAdapter extends AbstractAdapter {

	private static HttpClient httpClient = null;
	
	private static String ip = null;
	
	private static final int RETRY_MAX = 3;
	
	// type：飞机类型 carrierKey: 运营商简称英文 carrierName:运营商简称中文
	String carrierKey = null, carrierName = null, carrierFullName = null, startTime = null, endTime = null, flightNo = null, totalLowestPrice = null, agentName = null, totalHighestPrice = null, type = null;
	String cabinType = null, subCabinName = null, productName = null, price = null, originalPrice = null;
	String actuallyFlightNo = null, fromAirPort = null, fromAirPortName = null, toAirPort = null, toAirPortName = null;
	String stopCity;//经停城市
	Date arrTime,leaveTime;//到达时间，离开时间
	Long stayTime;//经停时间
	
	private static String carrKey = "TG";
	private static String carrName = "泰航";
	private static String carrFullName = "泰国航空";
	

	Float taxPriceFloat,originalPriceFloat;
	
	public ThaiAirAdapter(TaskModel taskQueue) {
		super(taskQueue);
		// TODO Auto-generated constructor stub
	}

	@Override
	public List<Object> paraseToVo(Object fetchObject) throws Exception {
		if(fetchObject == null){
			return null;
		}
		switch (getRouteType()) {
		case DOMESTIC_ONEWAYTRIP:
		case INTERNATIONAL_ONEWAY:
			return parseOneway(fetchObject);
		default:
			return parseRoundway(fetchObject);
		}
	}

	@Override
	public String getUrl() throws Exception {

		return null;
	}

	@Override
	public Object fetch(String url) throws Exception {
		switch (getRouteType()) {
		case DOMESTIC_ONEWAYTRIP:
		case INTERNATIONAL_ONEWAY:
			return fetchOneway();
		default:
			return fetchRoundway();
		}
	}

	private void destoryMyHttpClient(){
		if(httpClient != null){
			try {
				httpClient.getHttpConnectionManager().closeIdleConnections(3000);
			} catch (Exception e) {
				logger.error(e.getLocalizedMessage());
			}
		}
	}
	/**
	 * @Description: 抓取往返航线信息
	 * @param @return
	 * @return Object
	 * @author luofangyi
	 * @date 2014-4-28 下午4:56:25
	 */
	private Object fetchRoundway() {
//		List<FlightBill> billList = new ArrayList<FlightBill>();
		List<String> retList = new ArrayList<String>();
		List<String> cabinTypeList = this.getCabinType();
		WebClient webClient = null;
		HtmlPage pageFirst = null;

//		HtmlPage pageSencond;
//		HtmlForm fdffForm;
//		List<HtmlRadioButtonInput> cabinRadioList;//票价类型radio对象
//		List<HtmlRadioButtonInput> leaveRadioList;//出境航班列表radio
//		List<HtmlRadioButtonInput> backRadioList;//返程航班列表radio
		String secondHtml;
		Element fdffForm;
		Elements cabinElements;
		Elements leaveElements;
		Elements backElements;
		
		int retry = 0;//重试次数
		long begin = 0;
		for(String cabinType : cabinTypeList){
			try {
				try {
					webClient = this.getWebClientOfRedirectAndJavaScriptEnabled(null);
					logger.info("第" + retry + "次加载开始");
					begin = System.currentTimeMillis();
					pageFirst = this.fetchWayOfFirst(webClient, cabinType);
					logger.info("第" + retry + "次加载结束,耗时" + (System.currentTimeMillis() - begin) + "毫秒");
				} catch (Exception e) {
					logger.debug(e.getLocalizedMessage());
					rollbackProxyIp(false);
					pageFirst = null;
				}
				if(pageFirst == null || pageFirst.asXml().contains("400--Bad Request")){
					do {
						try {
							webClient = getWebClientByRetry();
							logger.info("第" + retry + "次加载开始");
							pageFirst = this.fetchWayOfFirst(webClient, "PE");// 豪华经济舱——单程中只有此选项
							logger.info("第" + retry + "次加载结束,耗时" + (System.currentTimeMillis() - begin) + "毫秒");
						} catch (Exception e) {
							logger.debug(e.getLocalizedMessage());
							pageFirst = null;
							rollbackProxyIp(false);
						}
						retry ++;
					} while (pageFirst == null && retry <= RETRY_MAX);
				}
				secondHtml = fetchWayOfSecondByHtmlPage(pageFirst.asXml(), true);
				Document doc = Jsoup.parse(secondHtml);
				fdffForm = doc.getElementById("FDFF_FORM");
				cabinElements = fdffForm.select("input[name=FamilyButton]");
				leaveElements = fdffForm.select("input[name=0fdffRadioOut]");
				backElements = fdffForm.select("input[name=1fdffRadioOut]");
			} catch (Exception e) {
				rollbackProxyIp(false);
				logger.error(e.getLocalizedMessage());
				continue;
			}
			if(cabinElements == null || leaveElements == null || backElements ==  null)
				return null;
			for(Element cabinElement : cabinElements){
				String cabin = this.getCabinOfParams(cabinElement);//用于参数构造
				for(Element leaveElement : leaveElements){
					String leave = this.getLeaveOrBackOfParams(leaveElement);//用于参数构造
					for(Element backElement : backElements){
						String back = this.getLeaveOrBackOfParams(backElement);//用于参数构造
						String threeHtml;
						try {
							threeHtml = this.fetchWayOfThree(secondHtml, "FDFF_FORM", cabinElement.attr("value"), leaveElement.attr("value"), backElement.attr("value"), "\"", cabin, leave, back);
							if(threeHtml != null && !threeHtml.equals("")){
								retList.add(threeHtml);
							}
						} catch (Exception e) {
							rollbackProxyIp(false);
							e.printStackTrace();
							continue;
						}
						
					}
				}
			}

		}
		rollbackProxyIp(true);
		setSourceInfo(retList);
		this.destoryMyHttpClient();
		return retList;
	}
	
	private List<String> getCabinType(){
		List<String> retList = new ArrayList<String>();
		//PE:豪华经济舱    FB:皇家头等、皇家商务舱   
		retList.add("PE");
		retList.add("FB");
		
		return retList;
	}
	
	/**  
	 * @Description: 获取票价类型单选按钮JS方法中第一个参数值
	 * @param @param cabinRadio
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-12 下午4:51:51 
	 */ 
	private String getCabinOfParams(Element cabinRadio){
		String regEx = "selectRecommendationSet(.+?)',";
		String cabin = this.getPatternString(cabinRadio.toString(), regEx);
		return cabin.length() >= 2 ? cabin.substring(2) : "0";
	}
	
	/**  
	 * @Description: 获取出境或返程单选按钮JS方法第一个参数
	 * @param @param radio
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-12 下午5:07:32 
	 */ 
	private String getLeaveOrBackOfParams(Element radio){
		String regEx = "selectFlight(.+?)',";
		String retStr = this.getPatternString(radio.toString(), regEx);
		return retStr.length() >= 2 ? retStr.substring(2) : "0";
	}

	/**
	 * @Description: 抓取单程航线信息
	 * @param @return
	 * @return Object
	 * @author luofangyi
	 * @throws Exception 
	 * @throws IOException
	 * @throws MalformedURLException
	 * @throws FailingHttpStatusCodeException
	 * @date 2014-4-28 下午4:55:36
	 */
	private Object fetchOneway() {

//		List<FlightBill> billList = new ArrayList<FlightBill>();
		List<String> retList = new ArrayList<String>();
		WebClient webClient = null;		
		HtmlPage pageFirst = null;
		
		String secondHtml;
		Element form;
		Elements elements;
		// 获取所有航班中可用的座位类别
		List<String> seatList;
		List<String> ticketList;

		int retry = 0;//重试次数
		long begin = 0;
		try {

			try {
				webClient = this.getWebClientOfRedirectAndJavaScriptEnabled(null);
				logger.info("第" + retry + "次加载开始");
				begin = System.currentTimeMillis();
				pageFirst = this.fetchWayOfFirst(webClient, "PE");// 豪华经济舱——单程中只有此选项
				logger.info("第" + retry + "次加载结束,耗时" + (System.currentTimeMillis() - begin) + "毫秒");
			} catch (Exception e) {
				rollbackProxyIp(false);
				logger.error(e.getLocalizedMessage());
				pageFirst = null;
			}
			if(pageFirst == null){
				do {
					try {
						webClient = getWebClientByRetry();
						logger.info("第" + retry + "次加载开始");
						begin = System.currentTimeMillis();
						pageFirst = this.fetchWayOfFirst(webClient, "PE");// 豪华经济舱——单程中只有此选项
						logger.info("第" + retry + "次加载结束,耗时" + (System.currentTimeMillis() - begin) + "毫秒");
					} catch (Exception e) {
						rollbackProxyIp(false);
						logger.debug(e.getLocalizedMessage());
						pageFirst = null;
					}
					retry ++;
				} while (pageFirst == null && retry <= RETRY_MAX);
			}
			secondHtml = fetchWayOfSecondByHtmlPage(pageFirst.asXml(), false);
			Document doc = Jsoup.parse(secondHtml);
			form = doc.getElementById("form");
			// 获取所有航班对象 —— 以供循环遍历
			elements = form.select("input[name=ROW_1]");
			// 获取所有航班中可用的座位类别
			seatList = this.cutSeatsOfEnable(secondHtml/*pageSencond*/);
			ticketList = this.getTicketCategory();
		} catch (Exception e) {
			rollbackProxyIp(false);
			logger.debug(e.getLocalizedMessage());
			return null;
		}
		
		if(elements == null || seatList == null || ticketList == null)
			return null;
		
		for(Element element : elements){
			String value = element.attr("value");
			for (String seat : seatList) {
				for (String ticket : ticketList) {
					String threeHtml;
					try {
						threeHtml = fetchWayOfThree(secondHtml, "form", value,
								seat, ticket, "\\?", "", "", "");
						if(threeHtml != null && !threeHtml.equals("")){
							retList.add(threeHtml);
						}
						
					} catch (Exception e) {
						rollbackProxyIp(false);
						e.printStackTrace();
						continue;
					}
				}
			}
		}
		rollbackProxyIp(true);
		setSourceInfo(retList);
		destoryMyHttpClient();
		return retList;
	}
	
	private void setSourceInfo(List<String> htmlList){
		StringBuffer sb = new StringBuffer("");
		for(String html : htmlList){
			super.appendPageContents(html);
		}
		int lenght = sb.toString().getBytes().length;
		setLenghtCount(lenght);
	}
	
	/**  
	 * @Description: 获取可用座位等级
	 * @param @param page
	 * @param @return 
	 * @return List<String>
	 * @author luofangyi
	 * @date 2014-5-7 下午5:49:10 
	 */ 
	private List<String> cutSeatsOfEnable(String page){
		
		String html = page;
		String regEx = "<td>.*?可用座位(.*?)</td>";
		html = this.getPatternString(html, regEx);
		regEx = ".*?<span class=\"seatsAvail\"(.*?)</span";
		
		return this.getPatternList(html, regEx, ">", "");
	}
	
	/**  
	 * @Description: 获取票价类型
	 * @param @return 
	 * @return List<String>
	 * @author luofangyi
	 * @date 2014-5-7 下午5:55:32 
	 */ 
	private List<String> getTicketCategory(){
		List<String> catList = new ArrayList<String>();
		catList.add("true");
		catList.add("false");
		return catList;
	}

	/**
	 * @Description: 抓取网页信息
	 * @param @param page
	 * @param @return
	 * @return List<FlightBill>
	 * @author luofangyi
	 * @date 2014-5-6 下午2:11:51
	 */
	private FlightBill spliteResultDate(String page) {

		FlightBill fbill = new FlightBill();
		
		Document doc = Jsoup.parse(page);
		//出境航线
		Element leaveContent = doc.getElementById("tabFgtReview_0");
		//返程航线
		Element backContent = doc.getElementById("tabFgtReview_1");
		try {
			if(leaveContent != null){
				// 出境航线信息
				fbill.setFlightList(this.cutFlightListInfo(leaveContent, "0"));
				if(backContent != null){
					fbill.setBackFlightList(this.cutFlightListInfo(backContent, "1"));
				}
				// 税费
				fbill.setTaxPrice(this.cutTaxPrice(doc.getElementById("taxes_ADT")));
				// 总价
				fbill.setMoney(this.cutMoney(doc.getElementById("spanTotalPriceOfAllPax")));
			} else {
				return null;
			}
		} catch (Exception e) {
			logger.debug(e.toString());
//			System.out.println(e.getLocalizedMessage());
			fbill.setMessage("抓取数据失败");
		}
		return fbill;
	}
	
	/**  
	 * @Description: 抓取税费
	 * @param @param flight
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-12 上午10:33:40 
	 */ 
	private String cutTaxPrice(Element flight){
		String money = flight.html().replace(")", "");
		return money.replace(",", "");//返回去除“,”号的字符串
	}
	
	/**  
	 * @Description: 抓取总金额
	 * @param @param flight
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-6 下午5:50:35 
	 */ 
	private String cutMoney(Element flight){
		
		String money = flight.html().replaceAll("[^\\d\\.]", "");
		return money;//返回去除“,”号的字符串
	}

	/**
	 * @Description: 抓取航线信息
	 * @param @param flight
	 * @param @return
	 * @param @throws Exception
	 * @return List<FlightInfo>
	 * @author luofangyi
	 * @date 2014-5-6 下午2:38:42
	 */
	public List<FlightInfo> cutFlightListInfo(Element flight, String pre) throws Exception {
		// TODO 判断是否有中转
		Elements transferElements = flight.select("td.border");

		int size = transferElements.size() != 0 ? transferElements.size() : 0;

		if(size == 0){//直达航班
			return this.directFlight(flight, pre);
		} else {//有中转情况
			return this.transferFlight(flight, size, pre);
		}
	}
	
	/**  
	 * @Description: 直达航线
	 * @param @param flight
	 * @param @return 
	 * @return List<FlightInfo>
	 * @author luofangyi
	 * @throws ParseException 
	 * @date 2014-5-6 下午2:51:11 
	 */ 
	private List<FlightInfo> directFlight(Element flight, String pre) throws ParseException{
		List<FlightInfo> flightList = new ArrayList<FlightInfo>();
		FlightInfo flightInfo = null;
		
		flightInfo = new FlightInfo();

		//TODO 目标出发、到达城市
		this.setCityInfo(flightInfo, flight);
		// TODO 航班日期
		this.setEachFlightDate(flightInfo, flight, 0);
		// TODO 航班号、航空公司（泰国航空）
		this.setFlightNoAndCompany(flightInfo, flight, pre, "0");
		// TODO 机型
		this.setFlightType(flightInfo, flight, pre, "0");
		// TODO 票价类型
		this.setCabinType(flightInfo, flight, pre, "0");
		// TODO 航班启程、到达时间及城市
		this.setCityAndTimeByStartAndArrive(flightInfo, flight, 0, 0);

		flightList.add(flightInfo);
		return flightList;
	}
	
	/**  
	 * @Description: 设置起始城市信息
	 * @param @param flightInfo
	 * @param @param flight
	 * @param @param type 0 : 起点城市      1 ： 终点城市
	 * @return void
	 * @author luofangyi
	 * @date 2014-5-12 下午5:46:32 
	 */ 
	private void setCityInfo(FlightInfo flightInfo, Element flight){
		String regEx = "<th colspan=\"3\">(.+?)</th>";
		String[] citys = this.getPatternString(flight.html(), regEx).split("到");
		flightInfo.setStartCity(citys[0].trim());
		flightInfo.setArriveCity(citys.length == 2 ? citys[1] : "");
	}
	
	private void setCabinType(FlightInfo flightInfo, Element flight, String pre, String suf){
		String regEx = "id=\"segFareType_" + pre + "_" + suf + "\">(.+?)</td";
		String cabin = this.getPatternString(flight.html(), regEx);
		if(cabin.contains("</a>")){
			cabin = getCabinTypeOfSecond(cabin).replaceAll("</a>", "");
		}
		flightInfo.setCabinType(cabin);
	}
	
	private String getCabinTypeOfSecond(String html){
		String regEx = ";\">(.+)";
		
		return this.getPatternString(html, regEx).trim();
	}
	
	private void setFlightType(FlightInfo flightInfo, Element flight, String pre, String suf){
		String regEx = "id=\"segAircraft_" + pre + "_" + suf + "\">(.+?)<";
		flightInfo.setFlightType(this.getPatternString(flight.html(), regEx).trim());
	}
	
	/**  
	 * @Description: 设置航班启程、到达时间及城市
	 * @param @param flightInfo
	 * @param @param flight
	 * @param @param size 总记录数
	 * @param @param step 当前步长
	 * @return void
	 * @author luofangyi
	 * @throws ParseException 
	 * @date 2014-5-7 上午10:27:32 
	 */ 
	private void setCityAndTimeByStartAndArrive(FlightInfo flightInfo, Element flight, int size, int step) throws ParseException{
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
		
		Elements times = flight.select("td.nowrap");
		Element startCityElement = null;
		Element arriveCityElement = null;
		Elements dateEles = flight.getElementsMatchingOwnText("年\\d+月\\d+日");
		String date = dateEles.get(step).text().replaceAll("[年月]","-").replaceAll("[^\\d-]", "");
		String[] arr = date.split("-");
		StringBuilder sb = new StringBuilder();
		sb.append(arr[0]).append("-");
		if(arr[1].length() == 1){
			sb.append("0" + arr[1]);
		}else{
			sb.append(arr[1]);
		}
		sb.append("-");
		if(arr[2].length() == 1){
			sb.append("0" + arr[2]);
		}else{
			sb.append(arr[2]);
		}
		if(times.size() == 2 * (size + 1)){
			startCityElement = times.get(2 * step + 0).parent().parent();
			arriveCityElement = times.get(2 * step + 1).parent().parent();
			flightInfo.setStartDate(/*flightInfo.getFlightDate() + " " + */
					sb + " " + this.dealWithTime(times.get(2 * step + 0).html().trim()));
			String arriveTime = times.get(2 * step + 1).html().trim();
			if(arriveTime.contains("textSmaller")){
				String arriveStr = this.dealWithMoreDay(flightInfo.getFlightDate(), arriveTime);
				flightInfo.setArriveDate(sb + " " + arriveStr);
			} else {
				flightInfo.setArriveDate(/*flightInfo.getFlightDate() + " " + */sb + " " +this.dealWithTime(arriveTime));
			}
		}
		
		Calendar calendar = Calendar.getInstance();
		Date date1 = format.parse(flightInfo.getStartDate());
		Date date2 = format.parse(flightInfo.getArriveDate());
		if(!date2.after(date1)){
			calendar.setTime(date2);
			calendar.add(Calendar.DATE, 1);
			flightInfo.setArriveDate(format.format(calendar.getTime()));
		}
		
//		String html = flight.html();
		String regStartCity = "<td width=\"90%\">(.+?),";
		String regArriveCity = "<td>([^<td>][0-9A-Za-z].*?),";
		
		flightInfo.setStartCity(this.getPatternString(startCityElement.html(), regStartCity));
		flightInfo.setArriveCity(this.getPatternString(arriveCityElement.html(), regArriveCity));
		
//		if(size == 0){//直飞情况下的起始城市设置
//			flightInfo.setStartCity(this.getPatternString(html, regStartCity));
//			flightInfo.setArriveCity(this.getPatternString(html, regArriveCity));
//		} else {//有中转站情况下的起始城市设置
//			flightInfo.setStartCity(this.getPatternString(html, regStartCity, step));
//			flightInfo.setArriveCity(this.getPatternString(html, regArriveCity, step));
//		}
		
	}
	
	/**  
	 * @Description: 处理日期中包含nowrap标记情形
	 * @param @param timeStr
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-14 上午11:01:29 
	 */ 
	private String dealWithTime(String timeStr){
		
		String regEx = "nowrap\">(.+?)</td>";
		if(timeStr.contains("nowrap")){
			timeStr = this.getPatternString(timeStr, regEx).trim();
		}
		return timeStr;
	}
	
	private String dealWithMoreDay(String flightDate, String timeStr){
		
		String regEx;
		regEx = "(.+?)<span class";
		return this.getPatternString(timeStr, regEx);
	
	}
	
	/**  
	 * @Description: TODO
	 * @param @param flightInfo
	 * @param @param flight
	 * @param @param pre 前缀字符
	 * @param @param suf 后缀字符
	 * @return void
	 * @author luofangyi
	 * @date 2014-5-12 下午6:08:16 
	 */ 
	private void setFlightNoAndCompany(FlightInfo flightInfo, Element flight, String pre, String suf){
		String reg = "id=\"segAirline_" + pre + "_" + suf + "\".*?>(.+?)<";

		String target = this.getPatternStringNoReplace(flight.html(), reg);
		String[] targets = null;
		String flightNo = "";
		StringBuffer flightCompany = new StringBuffer("");
		if(target.contains("&nbsp;")){
			targets = target.split("&nbsp;");
		} else {
			targets = target.split(" ");
		}
		//航班号一般在最后一个数组中
		flightNo = targets[targets.length - 1];
		//拼凑航空公司名称
		for(int i = 0; i < targets.length - 1; i++){
			flightCompany.append(targets[i]).append(" ");
		}
		flightInfo.setFlightNo(flightNo);
		flightInfo.setFlightCompany(flightCompany.toString().trim());
	}
	
	private void setEachFlightDate(FlightInfo flightInfo, Element flight, int step){
		String reg = "<td width=\"83%\" class=\"textBold\" colspan=\"2\">(.+?)</td>";
		String flightDate = this.getPatternString(flight.html(), reg, step);
		if(flightDate == null || flightDate.equals("")){
			reg = "<td width=\"83%\" colspan=\"2\" class=\"textBold\">(.+?)</td>";
			flightDate = this.getPatternString(flight.html(), reg, step);
		}
		if(flightDate == null || flightDate.equals("")){
			reg = "<td width=\"83%\" colspan=\"2\" class=\"textBold\">(.+?)</td>";
			flightDate = this.getPatternString(flight.html(), reg, step);
		}
		if(flightDate == null || flightDate.equals("")){
			reg = "<td colspan=\"2\" width=\"83%\" class=\"textBold\">(.+?)</td>";
			flightDate = this.getPatternString(flight.html(), reg, step);
		}
		if(flightDate == null || flightDate.equals("")){
			reg = "<td colspan=\"2\" class=\"textBold\" width=\"83%\">(.+?)</td>";
			flightDate = this.getPatternString(flight.html(), reg, step);
		}
		if(flightDate == null || flightDate.equals("")){
			reg = "<td class=\"textBold\" width=\"83%\" colspan=\"2\">(.+?)</td>";
			flightDate = this.getPatternString(flight.html(), reg, step);
		}
		if(flightDate == null || flightDate.equals("")){
			reg = "<td class=\"textBold\" colspan=\"2\" width=\"83%\">(.+?)</td>";
			flightDate = this.getPatternString(flight.html(), reg, step);
		}
		String dateStr = flightDate.substring(0, flightDate.indexOf("星期"));
		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年MM月dd日");
		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
		try {
			dateStr = sdf2.format(sdf1.parse(dateStr.trim()));
		} catch (ParseException e) {
			e.printStackTrace();
		}
		flightInfo.setFlightDate(dateStr);
	}
	
	private void setStayCityAndTime(FlightInfo flightInfo, Element flight, int step){
		String reg = "<div class=\"textChangeAirport\">(.+?).</div>";
		String[] targetStr = this.getPatternString(flight.html(), reg, step).split("= ");
		flightInfo.setStayTime(targetStr.length == 2 ? targetStr[1] : targetStr[0]);
		flightInfo.setStayCity(flightInfo.getArriveCity());
		
	}
	
	/**  
	 * @Description: 返回正则表达式截取内容 —— 直飞
	 * @param @param content
	 * @param @param regEx
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-6 下午4:35:48 
	 */ 
	private String getPatternString(String content, String regEx){
		Pattern p=Pattern.compile(regEx, Pattern.DOTALL|Pattern.MULTILINE);
		Matcher m=p.matcher(content);
		String retStr = "";
		if(m.find()){
			retStr=  m.group(1);
		}
		return retStr.trim().replaceAll("&nbsp;", "");
	}
	
	/**  
	 * @Description: TODO
	 * @param @param content
	 * @param @param regEx
	 * @param @param replace 需替换字符
	 * @param @param replacement 指定的替换字符
	 * @param @return 
	 * @return List<String>
	 * @author luofangyi
	 * @date 2014-5-13 下午4:49:32 
	 */ 
	private List<String> getPatternList(String content, String regEx, String replace, String replacement){
		Pattern p=Pattern.compile(regEx, Pattern.DOTALL|Pattern.MULTILINE);
		Matcher m=p.matcher(content);
		List<String> strList = new ArrayList<String>();
		while(m.find()){
			strList.add(m.group(1).trim().replaceAll(replace, replacement));
		}
		return strList;
	}
	
	/**  
	 * @Description: 返回正则表达式截取内容  —— 不对结果进行替换操作
	 * @param @param content
	 * @param @param regEx
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-7 下午4:42:47 
	 */ 
	private String getPatternStringNoReplace(String content, String regEx){
		Pattern p=Pattern.compile(regEx, Pattern.DOTALL|Pattern.MULTILINE);
		Matcher m=p.matcher(content);
		String retStr = "";
		if(m.find()){
			retStr=  m.group(1);
		}
		return retStr.trim();
	}
	
	/**  
	 * @Description: 返回正则表达式截取内容 —— 中转
	 * @param @param content
	 * @param @param regEx
	 * @param @param step 当前中转次数
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-6 下午7:00:21 
	 */ 
	private String getPatternString(String content, String regEx, int step){
		Pattern p=Pattern.compile(regEx, Pattern.DOTALL|Pattern.MULTILINE);
		Matcher m=p.matcher(content);
		String retStr = "";
		boolean flag = false;
		for(int i = 0; i <= step; i ++){
			flag = m.find();
		}
		if(flag)
			retStr = m.group(1);
		return retStr.trim().replaceAll("&nbsp;", "");
	}
	
	/**  
	 * @Description: 中转航线
	 * @param @param flight
	 * @param @param size 中转次数
	 * @param @return 
	 * @return List<FlightInfo>
	 * @author luofangyi
	 * @throws ParseException 
	 * @date 2014-5-6 下午2:52:21 
	 */ 
	private List<FlightInfo> transferFlight(Element flight, int size, String pre) throws ParseException{
		
		List<FlightInfo> flightList = new ArrayList<FlightInfo>();
		FlightInfo flightInfo = null;
		
		for(int i = 0; i <= size; i ++){
			flightInfo = new FlightInfo();
			
			// TODO 航班日期，从每段航程中取
			this.setEachFlightDate(flightInfo, flight, i);
			// TODO 航班号、航空公司
			this.setFlightNoAndCompany(flightInfo, flight, pre, i + "");
			// TODO 机型
			this.setFlightType(flightInfo, flight, pre, i + "");
			// TODO 票价类型
			this.setCabinType(flightInfo, flight, pre, i + "");
			// TODO 航班启程、到达时间及城市
			this.setCityAndTimeByStartAndArrive(flightInfo, flight, size, i);
			// TODO 经停城市、时间(经停次数总少于size一次)
			if(i < size){
				this.setStayCityAndTime(flightInfo, flight, i);
			}

			flightList.add(flightInfo);
		}
		return flightList;
	}

	/**
	 * @Description: 拼装第二步请求所需参数List
	 * @param @param html
	 * @param @param isRound true： 表示返程  false： 表示单程
	 * @param @return
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-4 下午7:07:18
	 */
	private List<NameValuePair> spliteListOfSecond(HtmlPage html, boolean isRound) {

		List<NameValuePair> paramList = new ArrayList<NameValuePair>();
		Document doc = Jsoup.parse(html.asXml());
		paramList.add(new NameValuePair("ENCT", this
				.getValueByName(doc, "ENCT")));
		paramList
				.add(new NameValuePair("ENC", this.getValueByName(doc, "ENC")));
		paramList.add(new NameValuePair("EXTERNAL_ID", this.getValueByName(doc,
				"EXTERNAL_ID")));
		paramList.add(new NameValuePair("EMBEDDED_TRANSACTION", this
				.getValueByName(doc, "EMBEDDED_TRANSACTION")));
		paramList.add(new NameValuePair("SITE", this
				.getValueByName(doc, "SITE")));
		paramList.add(new NameValuePair("LANGUAGE", this.getValueByName(doc,
				"LANGUAGE")));
		if(isRound){
			paramList.add(new NameValuePair("PRICING_TYPE", this.getValueByName(doc,
					"PRICING_TYPE")));
		}

		return paramList;
	}
	
	private String spliteParamsOfSecond(String html, boolean isRound) {

		StringBuffer sb = new StringBuffer("");
		Document doc = Jsoup.parse(html);
		sb.append("ENCT").append("=").append(this.getValueByName(doc, "ENCT"))
				.append("&");
		sb.append("ENC").append("=").append(this.getValueByName(doc, "ENC"))
				.append("&");
		sb.append("EXTERNAL_ID").append("=")
				.append(this.getValueByName(doc, "EXTERNAL_ID")).append("&");
		sb.append("EMBEDDED_TRANSACTION").append("=")
				.append(this.getValueByName(doc, "EMBEDDED_TRANSACTION"))
				.append("&");
		sb.append("SITE").append("=").append(this.getValueByName(doc, "SITE"))
				.append("&");
		sb.append("LANGUAGE").append("=")
				.append(this.getValueByName(doc, "LANGUAGE")).append("&");

		if (isRound) {
			sb.append("PRICING_TYPE").append("=")
					.append(this.getValueByName(doc, "PRICING_TYPE"));
		}

		return sb.toString();
	}

	/**
	 * @Description: 根据名称获取值
	 * @param @param doc
	 * @param @param name
	 * @param @return
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-4 下午7:10:56
	 */
	private String getValueByName(Document doc, String name) {

		String value = "";
		Elements elments = doc.select("input[name=" + name + "]");
		for (Element e : elments) {
			value = e.attr("value");
		}
		return value;
	}

	/**
	 * @Description: 获取航班信息第一步url
	 * @param @param cabin
	 * @param @return
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-4 下午6:11:04
	 */
	private String fetchWayOfFirstOfUrl(String cabin) {

		StringBuffer url = new StringBuffer(
				"http://www.thaiair.com/booking/Availability.htm?TRAVELLER_TYPE_1=ADT&LANGUAGE=CN&COUNTRY=CHINESE&TRAVELLER_TYPE_2=&TRAVELLER_TYPE_3=&TRAVELLER_TYPE_4=&TRAVELLER_TYPE_5=&TRAVELLER_TYPE_6=&TRAVELLER_TYPE_7=&TRAVELLER_TYPE_8=&TRAVELLER_TYPE_9=&");
		url.append("B_DATE_1=").append(
				this.formatDate(taskQueue.getFlightDate(), 0));
		
		url.append("&B_LOCATION_1=").append(taskQueue.getFromCity());
		url.append("&E_LOCATION_1=").append(taskQueue.getToCity());
		if (taskQueue.getIsReturn() == 0) {
			url.append("&TRIP_TYPE=").append("O");
			url.append("&B_DATE_2=").append(
					this.formatDate(taskQueue.getFlightDate(), 0));
		} else {
			url.append("&TRIP_TYPE=").append("R");
			url.append("&B_DATE_2=").append(
					this.formatDate(taskQueue.getReturnGrabDate(), 1));
		}
		url.append("&CABINCLASS=").append(cabin);

		return url.toString();
	}

	/**
	 * @Description: TODO
	 * @param @param date
	 * @param @param type 0 : 单程 1 : 返程
	 * @param @return
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-4 下午6:13:32
	 */
	private String formatDate(String date, int type) {

		if (date == null || date.trim().equals("")) {
			return "";
		}
		if (type == 0) {
			date += "0000";
		} else {
			date += "0100";
		}

		return date.replaceAll("-", "");
	}

	/**
	 * @Description: 获取支持自动跳转及执行javascript能力的WebClient
	 * @param @param bv
	 * @param @return
	 * @return WebClient
	 * @author luofangyi
	 * @date 2014-5-4 下午6:05:07
	 */
	private WebClient getWebClientOfRedirectAndJavaScriptEnabled(
			BrowserVersion bv) {

		
		WebClient webClient = this.getWebClient();
		this.setWebClientParamter(webClient);
		return webClient;
	}
	
	private void setWebClientParamter(WebClient webClient){
		webClient.getOptions().setRedirectEnabled(true);
		webClient.getOptions().setJavaScriptEnabled(true);
		webClient.getOptions().setCssEnabled(false);
		webClient.getOptions().setAppletEnabled(false);
		webClient.getOptions().setThrowExceptionOnScriptError(false);
		webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
		webClient.getOptions().setActiveXNative(false);
		webClient.getOptions().setTimeout(60000);
//		webClient.getOptions().setUseInsecureSSL(true);
		webClient.getCookieManager().setCookiesEnabled(false);
	}
	
	private WebClient getWebClientByRetry(){
		this.switchProxyipByWebClient();
		WebClient webClient = this.getWebClient();
		this.setWebClientParamter(webClient);
		return webClient;
	}

	/**
	 * @Description: 抓取航线信息 —— 第一步
	 * @param @return
	 * @return String
	 * @author luofangyi
	 * @throws IOException
	 * @throws FailingHttpStatusCodeException
	 * @date 2014-5-4 下午5:47:59
	 */
	private HtmlPage fetchWayOfFirst(WebClient webClient, String cabin) throws FailingHttpStatusCodeException, IOException{
		
		return webClient.getPage(this.fetchWayOfFirstOfUrl(cabin));
	}
		
	/**  
	 * @Description: 根据URL获取页面内容
	 * @param @param url
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @throws IOException 
	 * @throws HttpException 
	 * @throws Exception 
	 * @date 2014-5-13 下午3:50:22 
	 */ 
	private String getHtmlByUrl(String url) throws IOException {
		String retStr = null;
		int retry = 0;//重试次数
		HttpClient httpclient = null;
		GetMethod httpget = null;
		InputStream in = null;
		int bufferSize = 1024 * 2;
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] data = new byte[bufferSize];
		int count = -1;
		try {
			httpclient = getHttpClientInstance();
			httpget = new GetMethod(url);
			httpclient.executeMethod(httpget);
			in = httpget.getResponseBodyAsStream();
			while ((count = in.read(data, 0, bufferSize)) != -1)
				outStream.write(data, 0, count);
			retStr = new String(outStream.toByteArray(), httpget.getResponseCharSet());
			if(retStr.contains("400--Bad Request")){
				throw new Exception("请求内容错误");
			}
		} catch (Exception e) {
			logger.debug(e.getLocalizedMessage());
			in = null;
		}
		if(in == null ){
			do {
				try {
					httpclient = getRetryHttpClient(httpclient);
					httpget = new GetMethod(url);
					httpclient.executeMethod(httpget);
					in = httpget.getResponseBodyAsStream();
					while ((count = in.read(data, 0, bufferSize)) != -1)
						outStream.write(data, 0, count);
					retStr = new String(outStream.toByteArray(), httpget.getResponseCharSet());
					if(retStr.contains("400--Bad Request")){
						throw new Exception("请求内容错误");
					}
				} catch (Exception e) {
					logger.debug(e.getLocalizedMessage());
					in = null;
				}
				
				retry ++;
			} while (retStr == null && retry <= RETRY_MAX);
		}
		
		
		return retStr;
   	}

	/**
	 * @Description: 抓取单程、往返航线信息 —— 第二步(返回HtmlPage形式数据)
	 * @param @param html
	 * @param @param webClient
	 * @param @param isRound true:表示往返     false : 表示单程
	 * @param @return
	 * @return HtmlPage
	 * @author luofangyi
	 * @throws IOException
	 * @throws FailingHttpStatusCodeException
	 * @date 2014-5-5 上午10:02:37
	 */
	private String fetchWayOfSecondByHtmlPage(String html, boolean isRound)
			throws Exception {

		String url = "https://wftc3.e-travel.com/plnext/thai6v9/Override.action?"
				+ this.spliteParamsOfSecond(html, isRound);

		return this.getHtmlByUrl(url);
	}
	
	/**  
	 * @Description: 抓取航班信息第三步
	 * @param @param page
	 * @param @param webClient
	 * @param @param formId FORM表单ID
	 * @param @param radio
	 * @param @param seat
	 * @param @param ticket
	 * @param @param endStr 正则表达式中结束字符
	 * @param @param cabinValue
	 * @param @param leaveValue
	 * @param @param backValue
	 * @param @return
	 * @param @throws Exception 
	 * @return HtmlPage
	 * @author luofangyi
	 * @date 2014-5-9 上午10:50:55 
	 */
	private String fetchWayOfThree(String html, String formId, String radio, String seat, String ticket, String endStr, String cabinValue, String leaveValue, String backValue) throws Exception{
		String url = "https://wftc3.e-travel.com/plnext/thai6v9/Fare.action;" + this.getJsessionidInAction(html, endStr) + "?" + this.spliteOfThree(html, formId, radio, seat, ticket, cabinValue, leaveValue, backValue);
		return this.getHtmlByUrl(url);
	}
	
	/**  
	 * @Description: 获取action中的jsessionid
	 * @param @param page
	 * @param @param endStr 终止标记
	 * @param @return 
	 * @return String
	 * @author luofangyi
	 * @date 2014-5-9 上午10:50:35 
	 */ 
	private String getJsessionidInAction(String page, String endStr){

		String regEx = "action=\"/plnext/thai6v9/Fare.action;(.+?)" + endStr;
		return this.getPatternString(page, regEx);
	}
	
	/**  
	 * @Description: 获取第三步请求参数
	 * @param @param page
	 * @param @param form FORM表单ID
	 * @param @param radio
	 * @param @param seat
	 * @param @param ticket	 
	 * @param @param cabinValue
	 * @param @param leaveValue
	 * @param @param backValue
	 * @param @return
	 * @param @throws Exception 
	 * @return List<NameValuePair>
	 * @author luofangyi
	 * @date 2014-5-9 上午10:50:11 
	 */ 
	private String spliteOfThree(String page, String form, String radio, String seat, String ticket, String cabinValue, String leaveValue, String backValue) throws Exception{
		HtmlCleaner cleaner = new HtmlCleaner();
		TagNode root = cleaner.clean(page);
		Map<String, String> params = new HashMap<String, String>();
		StringBuffer sb = new StringBuffer("");

		String xPath = "//form[@id='" + form + "']//input";
		Object[] inputs = root.evaluateXPath(xPath);
		for (Object input : inputs) {
			TagNode node = (TagNode) input;
			if (StringUtils.isBlank(node.getAttributeByName("name"))) {
				continue;
			}
			params.put(node.getAttributeByName("name"), node.getAttributeByName("value"));
		}

		if(form.equals("form")){//单程
			params.put("ROW_1", radio);
			params.put("CABIN", seat);
			params.put("RESTRICTION", ticket);
			params.put("SITE", "CAURCAUR");
			params.put("LANGUAGE", "CN");
		} else if(form.equals("FDFF_FORM")){//往返航班
			params.put("FamilyButton", radio);
			params.put("0fdffRadioOut", seat);
			params.put("1fdffRadioOut", ticket);
			
			params.put("RECOMMENDATION_ID_1", cabinValue);
			params.put("FLIGHT_ID_1", leaveValue);
			params.put("FLIGHT_ID_2", backValue);
			params.put("FLIGHTOUTBOUND", leaveValue);
			params.put("FLIGHTINBOUND", backValue);
			params.put("IS_DIRTY_OUTBOUND", "Y");
			params.put("IS_DIRTY_INBOUND", "Y");
			params.put("selectSortFlights_out", "N");
		}
		
		for(String key : params.keySet()){
			sb.append(key).append("=").append(params.get(key)).append("&");
		}
		return sb.toString();
	}
	
	@Override
	public boolean validateFetch(Object fetchObject) throws Exception {
		if(fetchObject == null){
			throw new Exception("没有抓取到泰航数据");
		}
		return true;
	}

	/**
	 * @Description: 解析往返航线信息
	 * @param @param fetchObject
	 * @param @return
	 * @return List<Object>
	 * @author luofangyi
	 * @throws Exception 
	 * @date 2014-4-28 下午4:57:00
	 */
	private List<Object> parseRoundway(Object fetchObject) throws Exception {
		List<FlightBill> billList = this.parseHtml(fetchObject);
		List<AbstractPlaneInfoEntity> retList = new ArrayList<AbstractPlaneInfoEntity>();
		
		//<去程航班号，航班信息> 一条航线所有航班号确定一个航班实体
		Map<String,AbstractPlaneInfoEntity> map = new HashMap<String, AbstractPlaneInfoEntity>();
		StringBuilder grFilghtNo = null;
		//<全程程航班号，返程航班信息> 一条航线所有航班号确定一个航班实体
		Map<String,AbstractPlaneInfoEntity> returnMap = new HashMap<String, AbstractPlaneInfoEntity>();
		StringBuilder grReturnFilghtNo = null;
		for (FlightBill bill : billList) {
			
			List<TransitEntity> transit = new ArrayList<TransitEntity>();
//			Set<StopOverEntity> stopSet = new HashSet<StopOverEntity>();
			
			taxPriceFloat = Float.parseFloat(bill.getTaxPrice() != null && !bill.getTaxPrice().equals("") ? bill.getTaxPrice() : "0");
			originalPriceFloat = Float.parseFloat(bill.getMoney() != null && !bill.getMoney().equals("") ? bill.getMoney() : "0");
			price = Float.toString(originalPriceFloat - taxPriceFloat);//裸价
			originalPrice =  Float.toString(originalPriceFloat);//实际支付价格（包括税费等）
			
			if(bill.getFlightList() != null){
				grFilghtNo = new StringBuilder();
				for (FlightInfo info : bill.getFlightList()) {// 出境
					grFilghtNo.append(info.getFlightNo());
					try {
						carrierFullName = info.getFlightCompany();
						startTime = info.getStartDate();
						endTime = info.getArriveDate();
						flightNo = info.getFlightNo();
						type = info.getFlightType();
						cabinType = info.getCabinType();
						fromAirPortName = info.getStartCity();
						toAirPortName = info.getArriveCity();
						stopCity = info.getStayCity();
						arrTime = DateUtil.StringToDate("yyyy-MM-dd HH:mm", info.getStartDate());
						leaveTime = DateUtil.StringToDate("yyyy-MM-dd HH:mm", info.getArriveDate());
						stayTime = this.timeToLong(info.getStayTime());
//							stopSet.add(this.builderStopOverEntity(stopCity, arrTime, leaveTime, stayTime, StopOverEntity.class));
						transit.add(this.setTransitEntity(flightNo, null,
								carrierKey, carrierName, carrierFullName,
								fromAirPort, fromAirPortName, toAirPort,
								toAirPortName, type, arrTime, leaveTime,
								stayTime));
					} catch (Exception e) {
						logger.debug(e.getLocalizedMessage());
					}
				}
			}
			
			//增加航班
			DoublePlaneInfoEntity entity  = null;
			String fightNoStr = grFilghtNo.toString();
			FlightInfo flightInfo = bill.getFlightList().get(0);
			FlightInfo lastInfo = bill.getFlightList().get(bill.getFlightList().size() - 1);
			if(!map.containsKey(fightNoStr)){
				entity = PlaneInfoEntityBuilder
					.buildPlaneInfo(taskQueue, carrKey, carrName,
							carrFullName, flightInfo.getStartDate(), lastInfo.getArriveDate(), flightInfo.getFlightNo(),
							null, null, null,
							flightInfo.getFlightType(), DoublePlaneInfoEntity.class);
				map.put(fightNoStr, entity);
				retList.add(entity);
			}else{
				entity = (DoublePlaneInfoEntity)map.get(fightNoStr);
			}
			
			CabinEntity cabin = null;
			Set<CabinEntity> cabins = entity.getCabins();
			boolean cabinInPlane = false;
			for(CabinEntity cab : cabins){
				if(cab.getCabinType().equals(flightInfo.getCabinType())){
					cabinInPlane = true;
					cabin = cab;
					break;
				}
			}
			//舱位没存过的，添加舱位信息
			if(!cabinInPlane){
				cabin = PlaneInfoEntityBuilder.buildCabinInfo(flightInfo.getCabinType(), null, null, null, null, null, null, CabinEntity.class);
				entity.getCabins().add(cabin);
			}
			if(entity.getTransits().isEmpty()){
				entity.getTransits().addAll(transit);
			}
			
			if(bill.getBackFlightList() != null){
				grReturnFilghtNo = new StringBuilder(grFilghtNo + "_");
				List<ReturnTransitEntity> returnTransitList = new ArrayList<ReturnTransitEntity>();
//				Set<ReturnStopOverEntity> returnStopOverSet = new HashSet<ReturnStopOverEntity>();
				
				for (FlightInfo info : bill.getBackFlightList()) {// 返程
					grReturnFilghtNo.append(info.getFlightNo());
					try {
						carrierFullName = info.getFlightCompany();
						startTime = info.getStartDate();
						endTime = info.getArriveDate();
						flightNo = info.getFlightNo();
						type = info.getFlightType();
						cabinType = info.getCabinType();
						fromAirPortName = info.getStartCity();
						toAirPortName = info.getArriveCity();
						stopCity = info.getStayCity();
						arrTime = DateUtil.StringToDate("yyyy-MM-dd HH:mm", info.getStartDate());
						leaveTime = DateUtil.StringToDate("yyyy-MM-dd HH:mm", info.getArriveDate());
						stayTime = this.timeToLong(info.getStayTime());
						
//						returnTransitList.add(PlaneInfoEntityBuilder.buildTransitEntity(flightNo, null, carrierKey, carrierName, carrierFullName, fromAirPort, fromAirPortName, toAirPort, toAirPortName, type, ReturnTransitEntity.class));
						returnTransitList.add(this.setReturnTransitEntity(flightNo, null,
								carrierKey, carrierName, carrierFullName,
								fromAirPort, fromAirPortName, toAirPort,
								toAirPortName, type, arrTime, leaveTime,
								stayTime));
//							returnStopOverSet.add(this.builderStopOverEntity(stopCity, arrTime, leaveTime, stayTime, ReturnStopOverEntity.class));
					} catch (Exception e) {
						logger.debug(e.getLocalizedMessage());
					}
				}
				
				ReturnDoublePlaneInfoEntity returnEntity = null;
				String returnFightNoStr = grReturnFilghtNo.toString();
				FlightInfo returnFlightInfo = bill.getBackFlightList().get(0);
				FlightInfo lastReturnInfo = bill.getBackFlightList().get(bill.getBackFlightList().size() - 1);
				if(!returnMap.containsKey(returnFightNoStr)){
					returnEntity = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, carrKey, carrName,
							carrFullName, returnFlightInfo.getStartDate(), lastReturnInfo.getArriveDate(), 
							returnFlightInfo.getFlightNo(),null, null, null,
						returnFlightInfo.getFlightType(), ReturnDoublePlaneInfoEntity.class);
					entity.getReturnPlaneInfos().add(returnEntity);
					returnMap.put(returnFightNoStr, returnEntity);
				}else{
					returnEntity = (ReturnDoublePlaneInfoEntity)returnMap.get(returnFightNoStr);
				}
				
				
				Set<ReturnCabinEntity> returnCabins = returnEntity.getReturnCabins();
				boolean reCabinInPlane = false;
				for(ReturnCabinEntity reCab : returnCabins){
					if(reCab.getCabinType().equals(returnFlightInfo.getCabinType())){
						reCabinInPlane = true;
						break;
					}
				}
				//舱位没存过的，添加舱位信息
				if(!reCabinInPlane){
					//现在舱位记的价格是来回的总价
					ReturnCabinEntity returnCabin = PlaneInfoEntityBuilder.buildCabinInfo(returnFlightInfo.getCabinType(), null, null,String.valueOf(taxPriceFloat),price, originalPrice, null, null, ReturnCabinEntity.class);
					returnCabins.add(returnCabin);
				}
				if(returnEntity.getReturnTransits().isEmpty()){
					returnEntity.getReturnTransits().addAll(returnTransitList);
				}
//				returnEntity.getReturnStopOvers().addAll(returnStopOverSet);
			}
//			entity.getStopOvers().addAll(stopSet);
			this.cleanProperty();
		}
		
		//设置来回舱位的关系
		for(Object obj : retList){
			DoublePlaneInfoEntity planEntity = (DoublePlaneInfoEntity)obj; 
			for(CabinEntity cabin : planEntity.getCabins()){
				for(ReturnDoublePlaneInfoEntity returnEntity : planEntity.getReturnPlaneInfos()){
					Set<ReturnCabinEntity> returnCabins = returnEntity.getReturnCabins();
					for(ReturnCabinEntity returnCabin : returnCabins){
						CabinRelationEntity relation = new CabinRelationEntity();
						relation.setPlaneInfoEntity(planEntity);
						relation.setFullPrice(returnCabin.getPrice());
						relation.setCabinId(cabin.getId());
						relation.setReturnCabinId(returnCabin.getId());
						relation.setTaxesPrice(returnCabin.getTaxesPrice());
						relation.setTotalFullPrice(returnCabin.getOriginalPrice());
						planEntity.getCabinRelations().add(relation);
					}
				}
			}
		}
		
		//只有去加回舱的总价。回舱总价设为空
		for(Object obj : retList){
			DoublePlaneInfoEntity planEntity = (DoublePlaneInfoEntity)obj; 
			for(ReturnDoublePlaneInfoEntity returnEntity : planEntity.getReturnPlaneInfos()){
				Set<ReturnCabinEntity> returnCabins = returnEntity.getReturnCabins();
				for(ReturnCabinEntity returnCabin : returnCabins){
					returnCabin.setPrice(null);
					returnCabin.setTaxesPrice(null);
					returnCabin.setOriginalPrice(null);
				}
			}
		}
		
		
		//设置最低价和最高价
		PlaneInfoEntityBuilder.buildLimitPrice(retList);
		List<Object> reList = new ArrayList<Object>();
		for(AbstractPlaneInfoEntity entity : retList){
			reList.add(entity);
		}
		return reList;
	}
	
	/**  
	 * @Description: 设置中转信息
	 * @param flightNo
	 * @param actuallyFlightNo
	 * @param carrierKey
	 * @param carrierName
	 * @param carrierFullName
	 * @param fromAirPort
	 * @param fromAirPortName
	 * @param toAirPort
	 * @param toAirPortName
	 * @param flightType
	 * @param arrTime
	 * @param leaveTime
	 * @param stayTime
	 * @return
	 * @throws Exception
	 * @author luofangyi
	 * @date 2014-5-30 下午5:32:50 
	 */ 
	private TransitEntity setTransitEntity(String flightNo,String actuallyFlightNo,String carrierKey,
			String carrierName,String carrierFullName,
			String fromAirPort,String fromAirPortName,String toAirPort,String toAirPortName,String flightType,
			Date arrTime, Date leaveTime, Long stayTime) throws Exception{
		TransitEntity tansitEntity = PlaneInfoEntityBuilder.buildTransitEntity(
				flightNo, null, carrierKey, carrierName,
				carrierFullName, fromAirPort, fromAirPortName,
				toAirPort, toAirPortName, flightType);
		tansitEntity.setStartTime(arrTime);
		tansitEntity.setEndTime(leaveTime);
		tansitEntity.setStayTime(stayTime);
		return tansitEntity;
	}
	
	/**  
	 * @Description: 设置中转返程信息
	 * @param flightNo
	 * @param actuallyFlightNo
	 * @param carrierKey
	 * @param carrierName
	 * @param carrierFullName
	 * @param fromAirPort
	 * @param fromAirPortName
	 * @param toAirPort
	 * @param toAirPortName
	 * @param flightType
	 * @param arrTime
	 * @param leaveTime
	 * @param stayTime
	 * @return
	 * @throws Exception
	 * @author luofangyi
	 * @date 2014-5-30 下午5:39:54 
	 */ 
	private ReturnTransitEntity setReturnTransitEntity(String flightNo,String actuallyFlightNo,String carrierKey,
			String carrierName,String carrierFullName,
			String fromAirPort,String fromAirPortName,String toAirPort,String toAirPortName,String flightType,
			Date arrTime, Date leaveTime, Long stayTime) throws Exception{
		ReturnTransitEntity tansitEntity = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, null, carrierKey, carrierName, carrierFullName, fromAirPort, fromAirPortName, toAirPort, toAirPortName, flightType, ReturnTransitEntity.class);
		tansitEntity.setStartTime(arrTime);
		tansitEntity.setEndTime(leaveTime);
		tansitEntity.setStayTime(stayTime);
		return tansitEntity;
	}

	private Long timeToLong(String dateStr){
		if(dateStr == null || dateStr.equals(""))
			return 0L;
		Date date;
		if(dateStr.contains("小时")){
			date = DateUtil.StringToDate("HH小时mm分", dateStr);
			
			int hour = date.getHours();
			int minute = date.getMinutes();
			return (long) ((hour * 60 + minute) * 60 * 1000);
		}
		return 0L;
	}
	
	private List<FlightBill> parseHtml(Object fetchObject){
		List<FlightBill> billList = new ArrayList<FlightBill>();
		List<String> htmlList = (List<String>)fetchObject;
		for(String html : htmlList){
			FlightBill fBill = this.spliteResultDate(html);
			if(fBill != null){
				billList.add(fBill);
			}
		}

//		System.out.println("==================start============");
//		for (FlightBill bf : billList) {
//			List<FlightInfo> fList = bf.getFlightList();
//			if (fList != null) {
//				for (FlightInfo info : fList) {
//					System.out.print("出境: " + info.toString());
//				}
//			}
//			List<FlightInfo> backList = bf.getBackFlightList();
//			if (backList != null) {
//				System.out.println();
//				for (FlightInfo info : fList) {
//					System.out.print("返程: " + info.toString());
//				}
//			}
//			System.out.println();
//			System.out.println(bf.getMoney());
//			System.out.println(bf.getTaxPrice());
//		}
//		System.out.println("===============end============");
		return billList;
	}
	/**
	 * @Description: 解析单程航线信息
	 * @param @param fetchObject
	 * @param @return
	 * @return List<Object>
	 * @author luofangyi
	 * @throws Exception 
	 * @date 2014-4-28 下午4:57:14
	 */
	private List<Object> parseOneway(Object fetchObject) throws Exception {
		
		List<FlightBill> billList = this.parseHtml(fetchObject);
		List<AbstractPlaneInfoEntity> retList = new ArrayList<AbstractPlaneInfoEntity>();
		
		//<全程航班号，航班信息> 一条航线所有航班号确定一个航班实体
		Map<String,AbstractPlaneInfoEntity> map = new HashMap<String, AbstractPlaneInfoEntity>();
		StringBuilder grFilghtNo = null;
		for (FlightBill bill : billList) {
			Set<TransitEntity> transit = new HashSet<TransitEntity>();
//			Set<StopOverEntity> stopSet = new HashSet<StopOverEntity>();
			
			taxPriceFloat = Float.parseFloat(bill.getTaxPrice() != null && !bill.getTaxPrice().equals("") ? bill.getTaxPrice() : "0");
			originalPriceFloat = Float.parseFloat(bill.getMoney() != null && !bill.getMoney().equals("") ? bill.getMoney() : "0");
			price = Float.toString(originalPriceFloat - taxPriceFloat);//裸价
			originalPrice = Float.toString(originalPriceFloat);//实际支付价格（包括税费等）
			if(bill.getFlightList() != null){
				grFilghtNo = new StringBuilder();
				for (FlightInfo info : bill.getFlightList()) {// 单程
					grFilghtNo.append(info.getFlightNo());
					try {
						carrierFullName = info.getFlightCompany();
						startTime = info.getStartDate();
						endTime = info.getArriveDate();
						flightNo = info.getFlightNo();
						type = info.getFlightType();
						cabinType = info.getCabinType();
						fromAirPortName = info.getStartCity();
						toAirPortName = info.getArriveCity();
						stopCity = info.getStayCity();
						arrTime = DateUtil.StringToDate("yyyy-MM-dd HH:mm",
								info.getStartDate());
						leaveTime = DateUtil.StringToDate("yyyy-MM-dd HH:mm",
								info.getArriveDate());
						stayTime = this.timeToLong(info.getStayTime());
						
						transit.add(this.setTransitEntity(flightNo, null,
								carrierKey, carrierName, carrierFullName,
								fromAirPort, fromAirPortName, toAirPort,
								toAirPortName, type, arrTime, leaveTime,
								stayTime));
	//							stopSet.add(this.builderStopOverEntity(stopCity,
	//									arrTime, leaveTime, stayTime,
	//									StopOverEntity.class));
							
					} catch (Exception e) {
						logger.debug(e.getLocalizedMessage());
					}
				}
			}
			SinglePlaneInfoEntity entity = null;
			String fightNoStr = grFilghtNo.toString();
			FlightInfo flightInfo = bill.getFlightList().get(0);
			FlightInfo lastInfo = bill.getFlightList().get(bill.getFlightList().size() - 1);
			if(!map.containsKey(fightNoStr)){
				entity = PlaneInfoEntityBuilder
						.buildPlaneInfo(taskQueue, carrKey, carrName,
								carrFullName, flightInfo.getStartDate(), lastInfo.getArriveDate(), flightInfo.getFlightNo()/*flightNo*/,
								null, null, null,
								flightInfo.getFlightType(), SinglePlaneInfoEntity.class);
				map.put(fightNoStr, entity);
				retList.add(entity);
			}else{
				entity = (SinglePlaneInfoEntity)map.get(fightNoStr);
			}
			//增加舱位信息
			//TODO  cabinType productName
			CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(flightInfo.getCabinType(),
					null, productName, String.valueOf(taxPriceFloat),price, originalPrice,
					null, null,CabinEntity.class);;
			entity.getCabins().add(cabin);
			//航班号已经加过中转信息的，不用再添加
			if(entity.getTransits().isEmpty()){
				entity.getTransits().addAll(transit);
			}
//			entity.getStopOvers().addAll(stopSet);
			this.cleanProperty();
		}
		
		//设置最低价和最高价
		PlaneInfoEntityBuilder.buildLimitPrice(retList);
		List<Object> reList = new ArrayList<Object>();
		for(AbstractPlaneInfoEntity entity : retList){
			reList.add(entity);
		}
		
		return reList;
	}
	
	/**  
	 * @Description: 清空属性值
	 * @author luofangyi
	 * @date 2014-5-30 下午6:08:06 
	 */ 
	private void cleanProperty() {
		this.carrierKey = null;
		this.carrierName = null;
		this.carrierFullName = null;
		this.startTime = null;
		this.endTime = null;
		this.flightNo = null;
		this.totalLowestPrice = null;
		this.agentName = null;
		this.totalHighestPrice = null;
		this.type = null;
		this.cabinType = null;
		this.subCabinName = null;
		this.productName = null;
		this.price = null;
		this.originalPrice = null;
		this.actuallyFlightNo = null;
		this.fromAirPort = null;
		this.fromAirPortName = null;
		this.toAirPort = null;
		this.toAirPortName = null;
		this.stopCity = null;// 经停城市
		this.arrTime = null;
		this.leaveTime = null;// 到达时间，离开时间
		this.stayTime = 0L;// 经停时间

		this.taxPriceFloat = 0F;
		this.originalPriceFloat = 0F;
		
	}
	
	private HttpClient getHttpClientInstance() throws Exception {
		if (httpClient == null) {
			synchronized (HttpClient.class) {
				if(httpClient == null){
					httpClient = new HttpClient();
					// 模拟浏览器，解决一些服务器程序只允许浏览器访问的问题
					httpClient.getParams().setParameter(CoreProtocolPNames.USER_AGENT,
							RandomBrowserVersion.getBV());
					httpClient.getParams().setParameter(
							CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
					httpClient.getParams().setIntParameter(
							CoreConnectionPNames.CONNECTION_TIMEOUT, 180000);
					httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 120000);
				}
			}
		}
		if (this.isUseProxyip() && proxyIp != null) {
			HttpHost proxyHost = new HttpHost(proxyIp.getIp(), proxyIp.getPort(), "http");
			httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
		}
		return httpClient;
	}
	
	private HttpClient getRetryHttpClient(HttpClient httpClient) {
		// 配置上允许使用代理
		if(this.isUseProxyip()){
			ipProvider = ipProvider == null ? ProxyIpProviderFactory.getDefaultProxyProvider() : ipProvider;
			proxyIp =  ipProvider.provideProxyIp(taskQueue.getChannel().getId());
			if(proxyIp != null){
				httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
						new HttpHost(proxyIp.getIp(),proxyIp.getPort(), "http"));
			}
		}
		return httpClient;
	}
	
	public static void main(String[] args) throws Exception {

		TaskModel taskModel = new TaskModel();
		taskModel.setFromCity("CAN");
		taskModel.setFromCityName("");
		taskModel.setToCity("KUL");
//		taskModel.setToCity("cnx");// 清迈
//		taskModel.setToCity("pus");//釜山
//		taskModel.setToCity("aal");//Aalborg 奥尔堡
//		taskModel.setToCity("ath");//雅典
//		taskModel.setToCity("lax");//洛杉矶
		taskModel.setToCityName("");
		taskModel.setFlightDate("2014-07-19");
		taskModel.setReturnGrabDate("2014-07-22");
		taskModel.setIsReturn(1);//往返
		//taskModel.setIsReturn(0);// 0:单程 1:往返
		taskModel.setIsInternational(1);// 国际

		ThaiAirAdapter adapter = new ThaiAirAdapter(taskModel);
		Object obj = adapter.fetch(null);
		List<Object> list = adapter.paraseToVo(obj);
		System.out.println(list);
	}
}
/**
 * @ClassName: FlightInfo
 * @Description: 航班信息
 * @author luofangyi
 * @date 2014-5-6 上午11:54:12
 * 
 */
class FlightInfo implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 3144929146143908157L;
	/**
	 * 航班日期
	 */
	private String flightDate;
	/**
	 * 起飞时间
	 */
	private String startDate;
	/**
	 * 起飞城市
	 */
	private String startCity;
	/**
	 * 到达时间
	 */
	private String arriveDate;
	/**
	 * 到达城市
	 */
	private String arriveCity;
	/**
	 * 航空公司
	 */
	private String airline;
	/**
	 * 航班号
	 */
	private String flightNo;
	/**
	 * 航空公司名称(英文)
	 */
	private String flightCompany;
	/**
	 * 飞机型号
	 */
	private String flightType;
	/**
	 * 舱位等级
	 */
	private String cabinType;
	/**
	 * 停留城市
	 */
	private String stayCity;
	/**
	 * 停留时间
	 */
	private String stayTime;

	public String getFlightDate() {
		return flightDate;
	}

	public void setFlightDate(String flightDate) {
		this.flightDate = flightDate;
	}

	public String getStartDate() {
		return startDate;
	}

	public void setStartDate(String startDate) {
		this.startDate = startDate;
	}

	public String getStartCity() {
		return startCity;
	}

	public void setStartCity(String startCity) {
		this.startCity = startCity;
	}

	public String getArriveDate() {
		return arriveDate;
	}

	public void setArriveDate(String arriveDate) {
		this.arriveDate = arriveDate;
	}

	public String getArriveCity() {
		return arriveCity;
	}

	public void setArriveCity(String arriveCity) {
		this.arriveCity = arriveCity;
	}

	public String getAirline() {
		return airline;
	}

	public void setAirline(String airline) {
		this.airline = airline;
	}

	public String getFlightNo() {
		return flightNo;
	}

	public void setFlightNo(String flightNo) {
		this.flightNo = flightNo;
	}

	public String getFlightType() {
		return flightType;
	}

	public void setFlightType(String flightType) {
		this.flightType = flightType;
	}

	public String getCabinType() {
		return cabinType;
	}

	public void setCabinType(String cabinType) {
		this.cabinType = cabinType;
	}

	public String getStayCity() {
		return stayCity;
	}

	public void setStayCity(String stayCity) {
		this.stayCity = stayCity;
	}

	public String getStayTime() {
		return stayTime;
	}

	public void setStayTime(String stayTime) {
		this.stayTime = stayTime;
	}

	public String getFlightCompany() {
		return flightCompany;
	}

	public void setFlightCompany(String flightCompany) {
		this.flightCompany = flightCompany;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((airline == null) ? 0 : airline.hashCode());
		result = prime * result
				+ ((arriveCity == null) ? 0 : arriveCity.hashCode());
		result = prime * result
				+ ((arriveDate == null) ? 0 : arriveDate.hashCode());
		result = prime * result
				+ ((cabinType == null) ? 0 : cabinType.hashCode());
		result = prime * result
				+ ((flightCompany == null) ? 0 : flightCompany.hashCode());
		result = prime * result
				+ ((flightDate == null) ? 0 : flightDate.hashCode());
		result = prime * result
				+ ((flightNo == null) ? 0 : flightNo.hashCode());
		result = prime * result
				+ ((flightType == null) ? 0 : flightType.hashCode());
		result = prime * result
				+ ((startCity == null) ? 0 : startCity.hashCode());
		result = prime * result
				+ ((startDate == null) ? 0 : startDate.hashCode());
		result = prime * result
				+ ((stayCity == null) ? 0 : stayCity.hashCode());
		result = prime * result
				+ ((stayTime == null) ? 0 : stayTime.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		FlightInfo other = (FlightInfo) obj;
		if (airline == null) {
			if (other.airline != null)
				return false;
		} else if (!airline.equals(other.airline))
			return false;
		if (arriveCity == null) {
			if (other.arriveCity != null)
				return false;
		} else if (!arriveCity.equals(other.arriveCity))
			return false;
		if (arriveDate == null) {
			if (other.arriveDate != null)
				return false;
		} else if (!arriveDate.equals(other.arriveDate))
			return false;
		if (cabinType == null) {
			if (other.cabinType != null)
				return false;
		} else if (!cabinType.equals(other.cabinType))
			return false;
		if (flightCompany == null) {
			if (other.flightCompany != null)
				return false;
		} else if (!flightCompany.equals(other.flightCompany))
			return false;
		if (flightDate == null) {
			if (other.flightDate != null)
				return false;
		} else if (!flightDate.equals(other.flightDate))
			return false;
		if (flightNo == null) {
			if (other.flightNo != null)
				return false;
		} else if (!flightNo.equals(other.flightNo))
			return false;
		if (flightType == null) {
			if (other.flightType != null)
				return false;
		} else if (!flightType.equals(other.flightType))
			return false;
		if (startCity == null) {
			if (other.startCity != null)
				return false;
		} else if (!startCity.equals(other.startCity))
			return false;
		if (startDate == null) {
			if (other.startDate != null)
				return false;
		} else if (!startDate.equals(other.startDate))
			return false;
		if (stayCity == null) {
			if (other.stayCity != null)
				return false;
		} else if (!stayCity.equals(other.stayCity))
			return false;
		if (stayTime == null) {
			if (other.stayTime != null)
				return false;
		} else if (!stayTime.equals(other.stayTime))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "FlightInfo [flightDate=" + flightDate + ", startDate="
				+ startDate + ", startCity=" + startCity + ", arriveDate="
				+ arriveDate + ", arriveCity=" + arriveCity + ", airline="
				+ airline + ", flightNo=" + flightNo + ", flightCompany="
				+ flightCompany + ", flightType=" + flightType + ", cabinType="
				+ cabinType + ", stayCity=" + stayCity + ", stayTime="
				+ stayTime + "]";
	}
}

/**
 * @ClassName: FlightBill
 * @Description: 航班详情清单
 * @author luofangyi
 * @date 2014-5-6 下午1:09:00
 * 
 */
class FlightBill implements Serializable {

	
	/**
	 * 
	 */
	private static final long serialVersionUID = -790734798362767467L;
	/**
	 * 出境航班信息List
	 */
	private List<FlightInfo> flightList;
	/**
	 * 返程航班信息
	 */
	private List<FlightInfo> backFlightList;
	/**
	 * 总计金额
	 */
	private String money;
	/**
	 * 税费
	 */
	private String taxPrice;
	/**
	 * 用于保存异常信息
	 */
	private String message;

	public List<FlightInfo> getFlightList() {
		return flightList;
	}

	public void setFlightList(List<FlightInfo> flightList) {
		this.flightList = flightList;
	}

	public String getMoney() {
		return money;
	}

	public void setMoney(String money) {
		this.money = money;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public List<FlightInfo> getBackFlightList() {
		return backFlightList;
	}

	public void setBackFlightList(List<FlightInfo> backFlightList) {
		this.backFlightList = backFlightList;
	}

	public String getTaxPrice() {
		return taxPrice;
	}

	public void setTaxPrice(String taxPrice) {
		this.taxPrice = taxPrice;
	}
}